﻿using System;
using System.Linq;
using System.Collections.Generic;
using System.Text;
using System.Xml.Serialization;

namespace Interfaces
{
    [XmlInclude(typeof(GPSData))]
    [XmlInclude(typeof(CellData))]
    [XmlInclude(typeof(WifiData))]
    public abstract class IPositionData
    {
        public abstract PositionOriginator Originator { get; }
        public abstract string Identifier { get; }
    }

    public abstract class IAbsoluteData : IPositionData
    {
        public abstract double Latitude { get; set; }
        public abstract double Longitude { get; set; }
        public abstract double Altitude { get; set; }
        public abstract DateTime Time { get; set; }
    }

    /// <summary>
    /// Interface to define common proximity entity for event notifications
    /// The hash value identifies the radio source unambiguously
    /// </summary>
    public abstract class IProximityData : IPositionData
    {
        public abstract string Name { get; set; }
        public abstract int RelativeSignalStrength { get; }
    }

    [Serializable]
    public class BluetoothData : IProximityData
    {
        [XmlAttribute]
        public byte[] BT_ADDR { get; set; }
        [XmlAttribute]
        public override string Name { get; set; }
        [XmlAttribute]
        public byte Rssi { get; set; }

        #region IProximityData Members

        public override int RelativeSignalStrength
        {
            get { return 0; }
        }

        #endregion

        #region IPositionData Members
        public override string Identifier
        {
            get
            {
                return BitConverter.ToString(BT_ADDR);
            }
        }

        public override PositionOriginator Originator
        {
            get { return PositionOriginator.Bluetooth; }
        }

        #endregion
    }

    [Serializable]
    public class CellData : IProximityData
    {
        [XmlAttribute]
        public int Id { get; set; }
        [XmlAttribute]
        public int Lac { get; set; }
        [XmlAttribute]
        public int Mcc { get; set; }
        [XmlAttribute]
        public int Mnc { get; set; }
        [XmlAttribute]
        public int SignalStrength { get; set; }

        #region IProximityData Members

        public override string Identifier
        {
            get { return this.Mcc + "-" + this.Mnc + "-" + this.Id; }
        }

        [XmlAttribute]
        public override string Name { get; set; }

        public override int RelativeSignalStrength
        {
            get { return SignalStrength; } // TODO: scale to 0..100 interval
        }

        #endregion

        #region IPositionData Members

        public override PositionOriginator Originator
        {
            get { return PositionOriginator.Cell; }
        }

        #endregion
    }

    [Serializable]
    public class WifiData : IProximityData
    {
        System.Text.ASCIIEncoding enc = new System.Text.ASCIIEncoding();
        [XmlAttribute]
        public byte[] MacAddress { get; set; }
        [XmlAttribute]
        public int Rssi { get; set; }

        #region IProximityData Members

        public override string Identifier
        {
            get { return BitConverter.ToString(MacAddress); }
        }
        [XmlAttribute]
        public override string Name { get; set; }

        public override int RelativeSignalStrength //a wifinél elméletileg -10 és -113 dB-es jelek lehetnek.
        {
            get
            {
                int rel =110+Rssi;

                if (rel > 100)
                    return 100;
                else if (rel < 0)
                    return 0;
                else return rel;            
            } // TODO: implement this!
        }

        #endregion

        #region IPositionData Members

        public override PositionOriginator Originator
        {
            get { return PositionOriginator.Wifi; }
        }

        #endregion
    }

    [Serializable]
    public class GPSData : IAbsoluteData
    {
        [XmlAttribute]
        public override string Identifier { 
            get
            {
                return String.Format("{0};{1};{2};{3}",
                                       Time.ToUniversalTime(),
                                       Latitude.ToString(),
                                       Longitude.ToString(),
                                       Altitude.ToString());
            }
        }
        [XmlAttribute]
        public override double Latitude { get; set; }
        [XmlAttribute]
        public override double Longitude { get; set; }
        [XmlAttribute]
        public override double Altitude { get; set; }
        [XmlAttribute]
        public override DateTime Time { get; set; }
        public static GPSData Empty
        {
            get { return new GPSData() { Latitude = double.NaN, Longitude = double.NaN, Time = DateTime.Now}; }
        }

        #region IPositionData Members

        public override PositionOriginator Originator
        {
            get { return PositionOriginator.Gps; }
        }

        #endregion

        public const double EarthRadiusInMiles = 3956.0;
        public const double EarthRadiusInKilometers = 6367.0;
        public static double ToRadian(double val) { return val * (Math.PI / 180); }
        public static double DiffRadian(double val1, double val2) { return ToRadian(val2) - ToRadian(val1); }
        public static double CalculateDistance(IAbsoluteData p1, IAbsoluteData p2)
        {
            return EarthRadiusInKilometers * 2 * Math.Asin(Math.Min(1, Math.Sqrt((Math.Pow(Math.Sin((DiffRadian(p1.Latitude, p2.Latitude)) / 2.0), 2.0) + Math.Cos(ToRadian(p1.Latitude)) * Math.Cos(ToRadian(p2.Latitude)) * Math.Pow(Math.Sin((DiffRadian(p1.Longitude, p2.Longitude)) / 2.0), 2.0)))));
        }

        public static double CalculateDistance(IAbsoluteData p1, double lat, double lon)
        {
            return EarthRadiusInKilometers * 2 * Math.Asin(Math.Min(1, Math.Sqrt((Math.Pow(Math.Sin((DiffRadian(p1.Latitude, lat)) / 2.0), 2.0) + Math.Cos(ToRadian(p1.Latitude)) * Math.Cos(ToRadian(lat)) * Math.Pow(Math.Sin((DiffRadian(p1.Longitude, lon)) / 2.0), 2.0)))));
        }
    }
}
